Atklājiet īpašumu testēšanu ar Python bibliotēku Hypothesis. Pārejiet no testiem, kas balstīti uz piemēriem, lai atrastu malas gadījumus un izveidotu izturīgāku programmatūru.
Tālāk par vienības testiem: Padziļināts skatījums uz īpašumu testēšanu ar Python bibliotēku Hypothesis
Programmatūras izstrādes pasaulē testēšana ir kvalitātes pamats. Desmitiem gadu dominējošā paradigma ir bijusi uz piemēriem balstīta testēšana. Mēs rūpīgi izstrādājam ievades, definējam sagaidāmās izvades un rakstām apgalvojumus, lai pārbaudītu, vai mūsu kods darbojas paredzētajā veidā. Šī pieeja, kas sastopama tādos karkasos kā unittest
un pytest
, ir jaudīga un būtiska. Bet ko darīt, ja es jums teiktu, ka pastāv papildu pieeja, kas var atklāt kļūdas, kuras jūs nekad neesat domājuši meklēt?
Laipni lūgti īpašumu testēšanas pasaulē – paradigmā, kas maina fokusu no konkrētu piemēru testēšanas uz jūsu koda vispārējo īpašumu pārbaudīšanu. Un Python ekosistēmā nenoliedzams šīs pieejas čempions ir bibliotēka ar nosaukumu Hypothesis.
Šī visaptverošā rokasgrāmata jūs aizvedīs no pilnīga iesācēja līdz pārliecinātam īpašumu testēšanas praktiķim ar Hypothesis. Mēs izpētīsim pamatkonceptus, iedziļināsimies praktiskos piemēros un iemācīsimies integrēt šo jaudīgo rīku savā ikdienas izstrādes darbplūsmā, lai veidotu izturīgāku, uzticamāku un kļūdām mazāk pakļautu programmatūru.
Kas ir īpašumu testēšana? Domāšanas maiņa
Lai saprastu Hypothesis, vispirms mums ir jāapgūst īpašumu testēšanas pamatideja. Salīdzināsim to ar tradicionālo, uz piemēriem balstītu testēšanu, ko mēs visi zinām.
Uz piemēriem balstīta testēšana: Pazīstamais ceļš
Iedomājieties, ka esat uzrakstījis pielāgotu kārtošanas funkciju my_sort()
. Izmantojot uz piemēriem balstītu testēšanu, jūsu domāšanas process būtu:
- „Pārbaudīsim to ar vienkāršu, sakārtotu sarakstu.” ->
assert my_sort([1, 2, 3]) == [1, 2, 3]
- „Kā būtu ar apgrieztā secībā esošu sarakstu?” ->
assert my_sort([3, 2, 1]) == [1, 2, 3]
- „Kā būtu ar tukšu sarakstu?” ->
assert my_sort([]) == []
- „Saraksts ar dublikātiem?” ->
assert my_sort([5, 1, 5, 2]) == [1, 2, 5, 5]
- „Un saraksts ar negatīviem skaitļiem?” ->
assert my_sort([-1, -5, 0]) == [-5, -1, 0]
Tas ir efektīvi, taču tam ir fundamentāls ierobežojums: jūs testējat tikai tos gadījumus, par kuriem spējat iedomāties. Jūsu testi ir tikai tik labi, cik jūsu iztēle. Jūs varat palaist garām malas gadījumus, kas saistīti ar ļoti lieliem skaitļiem, peldošās komatslēgas precizitātes kļūdām, specifiskām Unicode rakstzīmēm vai sarežģītām datu kombinācijām, kas rada negaidītu uzvedību.
Īpašumu testēšana: Domāšana invariantos
Īpašumu testēšana apgriež scenāriju. Tā vietā, lai sniegtu konkrētus piemērus, jūs definējat funkcijas īpašumus jeb invariantus – likumus, kas jāievēro visām derīgām ievadēm. Mūsu funkcijai my_sort()
šie īpašumi varētu būt:
- Izvade ir sakārtota: Jebkuram skaitļu sarakstam katrs elements izvades sarakstā ir mazāks vai vienāds ar to, kas seko tam.
- Izvade satur tos pašus elementus kā ievade: Sakārtotais saraksts ir tikai sākotnējā saraksta permutācija; netiek pievienots vai zaudēts neviens elements.
- Funkcija ir idempotenta: Jau sakārtota saraksta kārtošana to nedrīkst mainīt. Tas ir,
my_sort(my_sort(kaut-kāds-saraksts)) == my_sort(kaut-kāds-saraksts)
.
Ar šo pieeju jūs nerakstāt testu datus. Jūs rakstāt likumus. Pēc tam ļaujat karkasam, piemēram, Hypothesis, ģenerēt simtiem vai tūkstošiem nejaušu, daudzveidīgu un bieži vien viltīgu ievades, lai mēģinātu pierādīt jūsu īpašumus nepareizus. Ja tas atrod ievadi, kas pārkāpj īpašumu, tas ir atradis kļūdu.
Iepazīstinām ar Hypothesis: Jūsu automatizētais testu datu ģenerators
Hypothesis ir vadošā Python īpašumu testēšanas bibliotēka. Tā ņem jūsu definētos īpašumus un veic smago darbu, ģenerējot testu datus, lai tos izaicinātu. Tā nav tikai nejauša datu ģenerators; tas ir gudrs un jaudīgs rīks, kas izstrādāts, lai efektīvi atrastu kļūdas.
Galvenās Hypothesis iespējas
- Automātiska testu gadījumu ģenerēšana: Jūs definējat nepieciešamo datu *formu* (piemēram, „veselo skaitļu saraksts”, „tikai burtus saturošs teksts”, „datums un laiks nākotnē”), un Hypothesis ģenerē plašu piemēru klāstu, kas atbilst šai formai.
- Gudrā „samazināšana” (Shrinking): Šī ir maģiskā funkcija. Kad Hypothesis atrod neveiksmīgu testu gadījumu (piemēram, 50 sarežģītu skaitļu saraksts, kas sabojā jūsu kārtošanas funkciju), tā neziņo tikai par šo milzīgo sarakstu. Tā gudri un automātiski vienkāršo ievadi, lai atrastu mazāko iespējamo piemēru, kas joprojām izraisa neveiksmi. Tā vietā, lai būtu 50 elementu saraksts, tā var ziņot, ka neveiksme notiek tikai ar
[inf, nan]
. Tas padara atkļūdošanu neticami ātru un efektīvu. - Nevainojama integrācija: Hypothesis nevainojami integrējas ar populāriem testēšanas karkasiem, piemēram,
pytest
ununittest
. Jūs varat pievienot īpašumu testus blakus saviem esošajiem, uz piemēriem balstītajiem testiem, nemainot savu darbplūsmu. - Bagātīga stratēģiju bibliotēka: Tā nāk ar plašu iebūvēto „stratēģiju” kolekciju, lai ģenerētu visu, sākot no vienkāršiem veseliem skaitļiem un tekstiem līdz sarežģītām, ligzdotām datu struktūrām, datumiem un laikiem ar laika zonas informāciju, un pat NumPy masīviem.
- Valsts testēšana (Stateful Testing): Sarežģītākām sistēmām Hypothesis var testēt darbību secības, lai atrastu kļūdas stāvokļa pāreju laikā, kas ir noticis, izmantojot uz piemēriem balstītu testēšanu.
Sākšana: Jūsu pirmais Hypothesis tests
Apskatīsim to praksē. Labākais veids, kā saprast Hypothesis, ir redzēt to darbībā.
Instalēšana
Vispirms jums būs jāinstalē Hypothesis un jūsu izvēlētais testu izpildītājs (mēs izmantosim pytest
). Tas ir tik vienkārši kā:
pip install pytest hypothesis
Vienkāršs piemērs: Absolūtā vērtības funkcija
Apskatīsim vienkāršu funkciju, kurai vajadzētu aprēķināt skaitļa absolūto vērtību. Nedaudz kļūdaina implementācija varētu izskatīties šādi:
# failā `my_math.py` def custom_abs(x): """Pielāgota absolūtā vērtības funkcijas implementācija.""" if x < 0: return -x return x
Tagad uzrakstīsim testu failu test_my_math.py
. Vispirms tradicionālā pytest
pieeja:
# test_my_math.py (Balstīts uz piemēriem) def test_abs_positive(): assert custom_abs(5) == 5 def test_abs_negative(): assert custom_abs(-5) == 5 def test_abs_zero(): assert custom_abs(0) == 0
Šie testi iziet. Mūsu funkcija izskatās pareiza, balstoties uz šiem piemēriem. Bet tagad uzrakstīsim īpašumu testu ar Hypothesis. Kāds ir absolūtās vērtības funkcijas galvenais īpašums? Rezultāts nekad nedrīkst būt negatīvs.
# test_my_math.py (Balstīts uz īpašumiem ar Hypothesis) from hypothesis import given from hypothesis import strategies as st from my_math import custom_abs @given(st.integers()) def test_abs_property_is_non_negative(x): """Īpašums: jebkura vesela skaitļa absolūtā vērtība vienmēr ir >= 0.""" assert custom_abs(x) >= 0
Sadaliņosim to:
from hypothesis import given, strategies as st
: Mēs importējam nepieciešamās sastāvdaļas.given
ir dekorators, kas parastu testa funkciju pārvērš par īpašumu testu.strategies
ir modulis, kurā atrodam mūsu datu ģeneratorus.@given(st.integers())
: Šis ir testa kodols.@given
dekorators norāda Hypothesis, ka šī testa funkcija jāizpilda vairākas reizes. Katrā izpildes reizē tā ģenerēs vērtību, izmantojot norādīto stratēģijust.integers()
, un nodros to kā argumentux
mūsu testa funkcijai.assert custom_abs(x) >= 0
: Šis ir mūsu īpašums. Mēs apgalvojam, ka neatkarīgi no tā, kādu veselu skaitlix
Hypothesis izdomās, mūsu funkcijas rezultātam jābūt lielākam vai vienādam ar nulli.
Kad jūs to izpildīsiet ar pytest
, tas, visticamāk, izies daudziem vērtībām. Hypothesis mēģinās 0, -1, 1, lielus pozitīvus skaitļus, lielus negatīvus skaitļus un daudz ko citu. Mūsu vienkāršā funkcija tos visus pareizi apstrādā. Tagad pamēģināsim citu stratēģiju, lai redzētu, vai varam atrast vājumu.
# Testēsim ar peldošās komatslēgas skaitļiem @given(st.floats()) def test_abs_floats_property(x): assert custom_abs(x) >= 0
Ja jūs to izpildīsiet, Hypothesis ātri atradīs neveiksmīgu gadījumu!
Falsifying example: test_abs_floats_property(x=nan) ... assert custom_abs(nan) >= 0 AssertionError: assert nan >= 0Hypothesis atklāja, ka mūsu funkcija, saņemot
float('nan')
(nav skaitlis), atgriež nan
. Apgalvojums nan >= 0
ir nepareizs. Mēs tikko atradām smalku kļūdu, kuru, iespējams, nebūtu domājuši testēt manuāli. Mēs varētu salabot mūsu funkciju, lai apstrādātu šo gadījumu, iespējams, izraisot ValueError
vai atgriežot specifisku vērtību.
Vēl labāk, ko darīt, ja kļūda bija ļoti specifiska peldošās komatslēgas skaitļa gadījumā? Hypothesis „samazinātājs” būtu ņēmis lielu, sarežģītu neveiksmīgu skaitli un samazinājis to līdz vienkāršākajai iespējamajai versijai, kas joprojām izraisa kļūdu.
Stratēģiju spēks: Jūsu testu datu izveide
Stratēģijas ir Hypothesis kodols. Tās ir receptes datu ģenerēšanai. Bibliotēkā ir plašs iebūvēto stratēģiju klāsts, un jūs varat tās kombinēt un pielāgot, lai ģenerētu praktiski jebkuru datu struktūru, ko vien varat iedomāties.
Izplatītas iebūvētas stratēģijas
- Skaitļi:
st.integers(min_value=0, max_value=1000)
: Ģenerē veselus skaitļus, pēc izvēles noteiktā diapazonā.st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False)
: Ģenerē peldošās komatslēgas skaitļus, ar precīzu kontroli pār speciālām vērtībām.st.fractions()
,st.decimals()
- Teksts:
st.text(min_size=1, max_size=50)
: Ģenerē Unicode virknes noteiktā garumā.st.text(alphabet='abcdef0123456789')
: Ģenerē virknes no noteikta rakstzīmju kopuma (piemēram, heksadecimāliem kodiem).st.characters()
: Ģenerē atsevišķas rakstzīmes.
- Kolekcijas:
st.lists(st.integers(), min_size=1)
: Ģenerē sarakstus, kur katrs elements ir vesels skaitlis. Pievērsiet uzmanību, kā mēs kā argumentu nododam citu stratēģiju! To sauc par kompozīciju.st.tuples(st.text(), st.booleans())
: Ģenerē pārus ar fiksētu struktūru.st.sets(st.integers())
st.dictionaries(keys=st.text(), values=st.integers())
: Ģenerē vārdnīcas ar norādītiem atslēgu un vērtību tipiem.
- Laika vērtības:
st.dates()
,st.times()
,st.datetimes()
,st.timedeltas()
. Šos var padarīt ar laika zonas informāciju.
- Dažādi:
st.booleans()
: ĢenerēTrue
vaiFalse
.st.just('constant_value')
: Vienmēr ģenerē vienu un to pašu vērtību. Noderīgi, lai veidotu sarežģītas stratēģijas.st.one_of(st.integers(), st.text())
: Ģenerē vērtību no vienas no norādītajām stratēģijām.st.none()
: Ģenerē tikaiNone
.
Stratēģiju kombinēšana un transformēšana
Hypothesis patiesā jauda slēpjas tās spējā veidot sarežģītas stratēģijas no vienkāršākām.
.map()
izmantošana
.map()
metode ļauj ņemt vērtību no vienas stratēģijas un pārveidot to kaut kam citam. Tas ir ideāli piemērots jūsu pielāgoto klašu objektu izveidošanai.
# Vienkārša datu klase from dataclasses import dataclass @dataclass class User: user_id: int username: str # Stratēģija User objektu ģenerēšanai user_strategy = st.builds( User, user_id=st.integers(min_value=1), username=st.text(min_size=3, alphabet='abcdefghijklmnopqrstuvwxyz') ) @given(user=user_strategy) def test_user_creation(user): assert isinstance(user, User) assert user.user_id > 0 assert user.username.isalpha()
.filter()
un assume()
izmantošana
Dažreiz jums ir nepieciešams noraidīt noteiktas ģenerētās vērtības. Piemēram, jums varētu būt nepieciešams veselu skaitļu saraksts, kura summa nav nulle. Jūs varētu izmantot .filter()
:
st.lists(st.integers()).filter(lambda x: sum(x) != 0)
Tomēr .filter()
lietošana var būt neefektīva. Ja nosacījums bieži ir nepareizs, Hypothesis var pavadīt ilgu laiku, mēģinot ģenerēt derīgu piemēru. Bieži vien labāka pieeja ir izmantot assume()
testa funkcijas iekšpusē:
from hypothesis import assume @given(st.lists(st.integers())) def test_something_with_non_zero_sum_list(numbers): assume(sum(numbers) != 0) # ... jūsu testu loģika šeit ...
assume()
saka Hypothesis: „Ja šis nosacījums netiek izpildīts, vienkārši noraidiet šo piemēru un mēģiniet vēlreiz.” Tas ir tiešāks un bieži vien efektīvāks veids, kā ierobežot jūsu testu datus.
st.composite()
izmantošana
Patiešām sarežģītai datu ģenerēšanai, kur viena ģenerētā vērtība ir atkarīga no citas, st.composite()
ir nepieciešamais rīks. Tas ļauj jums uzrakstīt funkciju, kas kā argumentu pieņem speciālu draw
funkciju, ko varat izmantot, lai pakāpeniski izgūtu vērtības no citām stratēģijām.
Klasisks piemērs ir saraksta un derīga indeksa uz to ģenerēšana.
@st.composite def list_and_index(draw): # Vispirms izvelciet ne-tukšu sarakstu my_list = draw(st.lists(st.integers(), min_size=1)) # Pēc tam izvelciet indeksu, kas garantēti ir derīgs šim sarakstam index = draw(st.integers(min_value=0, max_value=len(my_list) - 1)) return (my_list, index) @given(data=list_and_index()) def test_list_access(data): my_list, index = data # Šī piekļuve ir garantēti droša, pateicoties tam, kā mēs izveidojām stratēģiju element = my_list[index] assert element is not None # Vienkāršs apgalvojums
Hypothesis darbībā: Reālās pasaules scenāriji
Pielietosim šos konceptus reālistiskākām problēmām, ar kurām programmatūras izstrādātāji saskaras katru dienu.
Scenārijs 1: Datu serializācijas funkcijas testēšana
Iedomājieties funkciju, kas serializē lietotāja profilu (vārdnīcu) URL drošā virknē un citu, kas to deserializē. Galvenais īpašums ir tas, ka process jābūt pilnībā atgriezeniskam.
import json import base64 def serialize_profile(data: dict) -> str: """Serializē vārdnīcu uz URL drošu base64 virkni.""" json_string = json.dumps(data) return base64.urlsafe_b64encode(json_string.encode('utf-8')).decode('utf-8') def deserialize_profile(encoded_str: str) -> dict: """Deserializē virkni atpakaļ vārdnīcā.""" json_string = base64.urlsafe_b64decode(encoded_str.encode('utf-8')).decode('utf-8') return json.loads(json_string) # Tagad testam # Mums ir nepieciešama stratēģija, kas ģenerē ar JSON saderīgas vārdnīcas json_dictionaries = st.dictionaries( keys=st.text(), values=st.recursive(st.none() | st.booleans() | st.floats(allow_nan=False) | st.text(), lambda children: st.lists(children) | st.dictionaries(st.text(), children), max_leaves=10) ) @given(profile=json_dictionaries) def test_serialization_roundtrip(profile): """Īpašums: Kodēta profila deserializēšanai jāatgriež sākotnējais profils.""" encoded = serialize_profile(profile) decoded = deserialize_profile(encoded) assert profile == decoded
Šis viens tests spēcīgi ietekmēs mūsu funkcijas ar milzīgu datu klāstu: tukšas vārdnīcas, vārdnīcas ar ligzdotiem sarakstiem, vārdnīcas ar Unicode rakstzīmēm, vārdnīcas ar dīvainām atslēgām un daudz ko citu. Tas ir daudz rūpīgāk nekā rakstīt dažus manuālus piemērus.
Scenārijs 2: Kārtošanas algoritma testēšana
Atgriezīsimies pie mūsu kārtošanas piemēra. Šeit ir tas, kā jūs testētu iepriekš definētos īpašumus.
from collections import Counter def my_buggy_sort(numbers): # Ieviesīsim smalku kļūdu: tā izmet dublikātus return sorted(list(set(numbers))) @given(st.lists(st.integers())) def test_sorting_properties(numbers): sorted_list = my_buggy_sort(numbers) # Īpašums 1: Izvadne ir sakārtota for i in range(len(sorted_list) - 1): assert sorted_list[i] <= sorted_list[i+1] # Īpašums 2: Elementi ir vienādi (tas atradīs kļūdu) assert Counter(numbers) == Counter(sorted_list) # Īpašums 3: Funkcija ir idempotenta assert my_buggy_sort(sorted_list) == sorted_list
Kad jūs izpildīsiet šo testu, Hypothesis ātri atradīs neveiksmīgu piemēru 2. īpašumam, piemēram, numbers=[0, 0]
. Mūsu funkcija atgriež [0]
, un Counter([0, 0])
nav vienāds ar Counter([0])
. „Samazinātājs” nodrošinās, ka neveiksmīgais piemērs ir pēc iespējas vienkāršāks, padarot kļūdas cēloni uzreiz acīmredzamu.
Scenārijs 3: Valsts testēšana (Stateful Testing)
Objektiem ar iekšējo stāvokli, kas mainās laika gaitā (kā datu bāzes savienojums, iepirkumu grozs vai kešatmiņa), kļūdu atrašana var būt neticami sarežģīta. Lai izraisītu kļūdu, var būt nepieciešama specifiska operāciju secība. Hypothesis nodrošina RuleBasedStateMachine
tieši šim nolūkam.
Iedomājieties vienkāršu atmiņā esošas atslēgas-vērtības krātuves API:
class SimpleKeyValueStore: def __init__(self): self._data = {} def set(self, key, value): self._data[key] = value def get(self, key): return self._data.get(key) def delete(self, key): if key in self._data: del self._data[key] def size(self): return len(self._data)Mēs varam modelēt tās uzvedību un testēt to ar stāvokļa mašīnu:
from hypothesis.stateful import RuleBasedStateMachine, rule, Bundle class KeyValueStoreMachine(RuleBasedStateMachine): def __init__(self): super().__init__() self.model = {} self.sut = SimpleKeyValueStore() # Bundle() tiek izmantots, lai nodotu datus starp noteikumiem keys = Bundle('keys') @rule(target=keys, key=st.text(), value=st.integers()) def set_key(self, key, value): self.model[key] = value self.sut.set(key, value) return key @rule(key=keys) def delete_key(self, key): del self.model[key] self.sut.delete(key) @rule(key=st.text()) def get_key(self, key): model_val = self.model.get(key) sut_val = self.sut.get(key) assert model_val == sut_val @rule() def check_size(self): assert len(self.model) == self.sut.size() # Lai izpildītu testu, vienkārši veiciet klasi no mašīnas un unittest.TestCase # Pytest vidē var vienkārši piešķirt testu klases nosaukumam TestKeyValueStore = KeyValueStoreMachine.TestCaseHypothesis tagad izpildīs nejaušas
set_key
, delete_key
, get_key
un check_size
operāciju secības, nepārtraukti mēģinot atrast secību, kas izraisa kāda no apgalvojumiem kļūdu. Tā pārbaudīs, vai dzēsta atslēgas saņemšana darbojas pareizi, vai izmērs ir konsekvents pēc vairākiem iestatījumiem un dzēšanas, un daudzas citas situācijas, kuras jūs varētu neapdomāt testēt manuāli.
Labākās prakses un padomi
- Piemēru datu bāze: Hypothesis ir gudrs. Kad tā atrod kļūdu, tā saglabā neveiksmīgo piemēru lokālā direktorijā (
.hypothesis/
). Nākamreiz, kad jūs izpildīsiet savus testus, tā atkārtos neveiksmīgo piemēru vispirms, sniedzot jums tūlītēju atgriezenisko saiti, ka kļūda joprojām pastāv. Kad jūs to izlabosiet, piemērs vairs netiks atkārtots. - Testu izpildes kontrole ar
@settings
: Jūs varat kontrolēt daudzus testa izpildes aspektus, izmantojot@settings
dekoratoru. Jūs varat palielināt piemēru skaitu, iestatīt termiņu, cik ilgi viens piemērs var darboties (lai pārtvertu bezgalīgus ciklus), un izslēgt dažas veselības pārbaudes.@settings(max_examples=500, deadline=1000) # Izpildīt 500 piemērus, 1 sekundes termiņš @given(...) ...
- Neveiksmju reproducēšana: Katrs Hypothesis izpildījums izdrukā sēklas vērtību (piemēram,
@reproduce_failure('version', 'seed')
). Ja CI serveris atrod kļūdu, kuru nevarat reproducēt lokāli, varat izmantot šo dekoratoru ar norādīto sēklu, lai piespiestu Hypothesis izpildīt tieši to pašu piemēru secību. - Integrācija ar CI/CD: Hypothesis ir lieliski piemērota jebkurai nepārtrauktas integrācijas cauruļvadam. Tās spēja atrast slēptas kļūdas pirms tās nonāk ražošanā padara to par nenovērtējamu drošības tīklu.
Domāšanas maiņa: Domāšana īpašumos
Hypothesis pieņemšana ir vairāk nekā tikai jaunas bibliotēkas apguve; tā ir jaunas domāšanas par jūsu koda pareizību. Tā vietā, lai jautātu: „Kādas ievades man vajadzētu testēt?”, jūs sākat jautāt: „Kādas ir universālās patiesības par šo kodu?”
Šeit ir daži jautājumi, kas palīdzēs jums identificēt īpašumus:
- Vai ir pretēja operācija? (piemēram, serializēt/deserializēt, šifrēt/atšifrēt, saspiest/atspiest). Īpašums ir tāds, ka veicot operāciju un tās pretējo, tiek iegūta sākotnējā ievade.
- Vai operācija ir idempotenta? (piemēram,
abs(abs(x)) == abs(x)
). Funkcijas lietošana vairāk nekā vienu reizi jārada tādu pašu rezultātu kā lietošana vienu reizi. - Vai ir cits, vienkāršāks veids, kā aprēķināt to pašu rezultātu? Jūs varat testēt, ka jūsu sarežģītā, optimizētā funkcija rada tādu pašu izvadi kā vienkārša, acīmredzami pareiza versija (piemēram, testējot savu moderno kārtošanu pret Python iebūvēto
sorted()
). - Kas vienmēr jābūt patiesam par izvadi? (piemēram,
find_prime_factors
funkcijas izvadei jābūt tikai pirmskaitļiem, un to reizinājumam jābūt vienādam ar ievadi). - Kā mainās stāvoklis? (Valsts testēšanai) Kādi invarianšiem jābūt saglabātiem pēc jebkuras derīgas operācijas? (piemēram, iepirkumu grozā esošo preču skaits nekad nevar būt negatīvs).
Noslēgums: Jauns pārliecības līmenis
Īpašumu testēšana ar Hypothesis neaizstāj uz piemēriem balstītu testēšanu. Jums joprojām ir nepieciešami specifiski, ar roku rakstīti testi kritiski svarīgai biznesa loģikai un labi saprotamām prasībām (piemēram, „Lietotājam no X valsts jāredz cena Y”).
Tas, ko Hypothesis nodrošina, ir jaudīgs, automatizēts veids, kā izpētīt jūsu koda uzvedību un pasargāties no neparedzētiem malu gadījumiem. Tā darbojas kā nogurdams partneris, ģenerējot tūkstošiem testu, kas ir daudzveidīgāki un viltīgāki, nekā jebkurš cilvēks reāli varētu uzrakstīt. Definējot jūsu koda fundamentālos īpašumus, jūs izveidojat izturīgu specifikāciju, pret kuru Hypothesis var testēt, sniedzot jums jaunu pārliecības līmeni par jūsu programmatūru.
Nākamreiz, kad rakstīsiet funkciju, veltiet brīdi, lai padomātu tālāk par piemēriem. Jautājiet sev: „Kādi ir likumi? Kas vienmēr jābūt patiesam?” Tad ļaujiet Hypothesis veikt smago darbu, mēģinot tos salauzt. Jūs būsiet pārsteigti par to, ko tā atradīs, un jūsu kods būs labāks.